package com.iot08.api.aspect;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iot08.api.constant.Constant;
import com.iot08.common.utils.response.ResponseEnum;
import com.iot08.common.utils.response.ResponseMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @author cwj
 * @description
 * @date 2024/8/12 18:33
 **/
@Aspect
@Component
@Slf4j
public class SignatureValidation {


    /**
     * 验签切点(完整的找到设置的文件地址)
     */
    @Pointcut("@annotation(com.iot08.api.annotation.CheckAppSign)")
    private void verifyUserKey() {
    }

    /**
     * 开始验签
     */
    @Around("verifyUserKey()")
    public ResponseMap doBasicProfiling(ProceedingJoinPoint joinPoint) {
        try {
            Object[] params = joinPoint.getArgs();
            //获取请求参数
            Object param = params[0];
            JSONObject jsonObject = this.getJSONObject(param);
            JSONObject bodyForm = jsonObject.getJSONObject(Constant.BODY_FORM);
            //map对象
            SortedMap<String, String> data = new TreeMap<>();
            log.info("bodyForm："+bodyForm);
           //循环转换
            Iterator<String> it = bodyForm.keySet().iterator();
            // 遍历jsonObject数据，添加到Map对象
            while (it.hasNext()) {
                String key = String.valueOf(it.next());
                Object value = bodyForm.get(key);
                if (value != null && !ObjectUtils.isEmpty(value.toString())) {
                    data.put(key, value.toString());
                }
            }
            boolean check = false;
            if (param != null) {
                check = checkSign(data);
            }
            if (check) {
                return (ResponseMap) joinPoint.proceed();
            }
            return ResponseMap.fail(ResponseEnum.STATUS400.getCode(),"API.request.paramterError.sign.fail.msg");
        } catch (Throwable throwable) {
            log.error("参数签名验证出现异常，异常信息为：{}",throwable.getMessage());
            return ResponseMap.fail(ResponseEnum.STATUS400.getCode(),"API.request.paramterError.sign.exception.msg");
        }
    }

    /**
     * @description 验签对象转换，json字符串不能直接转换的，就用ObjectMapper转换
     * @author cwj
     * @param [param]
     * @return com.alibaba.fastjson.JSONObject
     * @date @time 2024/12/2 13:49
     **/
    private JSONObject getJSONObject(Object param) throws JsonProcessingException, UnsupportedEncodingException {
        JSONObject jsonObject;
        try {
            jsonObject = (JSONObject)JSONObject.toJSON(param);
        } catch (Exception ex) {
            if (ObjectUtils.isEmpty(param)) {
                return null;
            }
            ObjectMapper objectMapper = new ObjectMapper();
            String bodyStr = param.toString();
            jsonObject = objectMapper.readValue(bodyStr, JSONObject.class);
            JSONObject bodyForm = jsonObject.getJSONObject("bodyForm");
            String isIos = bodyForm.getString("isIos");
            if (!StringUtils.isEmpty(isIos) && isIos.equals("1")) {
                bodyStr = new String(bodyStr.getBytes("ISO-8859-1"), "UTF-8");
                jsonObject = objectMapper.readValue(bodyStr, JSONObject.class);
            }
        }
        return jsonObject;
    }

    /**
     * @description 验签
     * @author cwj
     * @param [result]
     * @return boolean
     * @date @time 2024/8/13 8:40
     **/
    boolean checkSign(SortedMap<String, String> result) throws Exception {
        // 校验时间戳
        String timestamp = result.get(Constant.TIMESTAMP);
        String sign = result.get(Constant.SIGN);
        if (StringUtils.isEmpty(timestamp)) {
            return false;
        }
        if (StringUtils.isEmpty(sign)) {
            return false;
        }
        //检验请求时间是否过长
       /* boolean validateTimeStamp = validateTimeStamp(Long.valueOf(timestamp));
        if (!validateTimeStamp) {
            return ResponseMap.fail(ResponseEnum.STATUS400.getCode(),"签名失败，请求时间与系统时间差异过大");
        }*/
        //校验签名
        boolean verifySign = verifySign(result, Constant.APP_SECRET);

        if (!verifySign) {
            log.info("签名 app_secret:{}",  Constant.APP_SECRET);
            return false;
        }
        return true;

    }

    /**
     * @param params 所有的请求参数都会在这里进行排序加密
     * @return 验证签名结果
     */
    public static boolean verifySign(SortedMap<String, String> params, String key) {
        String urlSign = params.get(Constant.SIGN);
        //System.out.println("=========sign："+urlSign);
        log.info("Url Sign : {}", urlSign);
        if (StringUtils.isEmpty(urlSign)) {
            return false;
        }
        // 把参数加密
        String paramsSign = getParamsSign(params, key);
        //System.out.println("===========生成的签："+paramsSign);
        log.info("Param Sign : {}", paramsSign);
        return !StringUtils.isEmpty(paramsSign) && urlSign.equals(paramsSign);
    }

    /**
     * @param params
     * @return 得到签名
     */
    public static String getParamsSign(SortedMap<String, String> params, String key) {
        // 要先去掉 Url 里的 Sign
        params.remove("sign");
        StringBuffer sb = new StringBuffer();
        params.forEach((k, v) -> {
            sb.append(k).append("=").append(v).append("&");
        });
        sb.append("key").append("=").append(key);
        try {
            log.info("参数内容：{}",sb);
            String sign = new String(sb.toString().getBytes(),  StandardCharsets.UTF_8);
            log.info("md5q前 : {}", sign);
            return DigestUtils.md5Hex(sign).toUpperCase();
        } catch (Exception e) {
            log.error("签名错误不支持utf-8编码");
        }
        return null;
    }

    /**
     * @description: 判断客户端的请求是否超过3分钟
     */
    private boolean validateTimeStamp(long timestamp) {
        log.info("System.currentTimeMillis:{}", System.currentTimeMillis());
        Long tims = (System.currentTimeMillis() - timestamp) / (1000 * 60);
        //验证时间戳是否超过3分钟
        if (Math.abs(tims) > 3) {
            return false;
        } else {
            return true;
        }
    }
}
